package com.songtech.uitest.util;

import org.openqa.selenium.*;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.util.Iterator;
import java.util.List;

public class AutoWait {


    private static final long timeOut = 10;

    private static WebDriverWait wait = null;

    private static WebDriver driver = null;


    public static void setWebDriverWait(WebDriver driver){
        wait = new WebDriverWait(driver, timeOut);
        AutoWait.driver = driver;
    }

    public static WebDriver getDriver(){
        return driver;
    }


    //等待页面上某元素出现，出现并可见时，才返回元素
    public static WebElement findByXpath(String path){
        //presence_of_element_located： 当我们不关心元素是否可见，只关心元素是否存在在页面中。
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(path)));
        //visibility_of_element_located： 当我们需要找到元素，并且该元素也可见。
        wait.until(ExpectedConditions.visibilityOf(element));
        wait.until(ExpectedConditions.elementToBeClickable(element));
        return element;
    }

    //等待页面上某元素组的出现，整个元素组出现(但不需要可见)时，才返回元素
    public static List<WebElement> findAllByXpath(String path){
        List<WebElement> list = wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.xpath(path)));
        return list;
    }

    public static WebElement findByCss(String path){
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(path)));
        wait.until(ExpectedConditions.visibilityOf(element));
        wait.until(ExpectedConditions.elementToBeClickable(element));
        return element;
    }

    public static List<WebElement> findAllByCss(String path){
        List<WebElement> list = wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.cssSelector(path)));
        return list;
    }

    public static WebElement findByID(String path){
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.id(path)));
        wait.until(ExpectedConditions.visibilityOf(element));
        wait.until(ExpectedConditions.elementToBeClickable(element));
        return element;
    }

    public static List<WebElement> findAllByID(String path){
        List<WebElement> list = wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.id(path)));
        return list;
    }

    public static WebElement findByName(String path){
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.name(path)));
        wait.until(ExpectedConditions.visibilityOf(element));
        wait.until(ExpectedConditions.elementToBeClickable(element));
        return element;
    }

    public static List<WebElement> findAllByName(String path){
        List<WebElement> list = wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.name(path)));
        return list;
    }

    public static WebElement findByTagName(String path){
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.tagName(path)));
        wait.until(ExpectedConditions.visibilityOf(element));
        wait.until(ExpectedConditions.elementToBeClickable(element));
        return element;
    }

    public static List<WebElement> findAllByTagName(String path){
        List<WebElement> list = wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.tagName(path)));
        return list;
    }







    //等待页面上某元素可成功访问时，才返回元素
    //通过已有元素再次定位另一元素的方式，获取另一元素
    private static WebElement findBy(WebElement element, String path, String way) {
        WebElement ele = null;
        boolean flag = false;
        for (int i = 0; i < 20; i++) {
            try {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                switch (way){
                    case "xpath":
                        element.findElement(By.xpath(path));
                        break;
                    case "css":
                        element.findElement(By.cssSelector(path));
                        break;
                    case "id":
                        element.findElement(By.id(path));
                        break;
                    case "name":
                        element.findElement(By.name(path));
                        break;
                    case "tagname":
                        element.findElement(By.tagName(path));
                        break;
                }
                flag = true;
            } catch (org.openqa.selenium.NoSuchElementException ex) {
                flag = false;
            }
            if (flag == true) {
                switch (way){
                    case "xpath":
                        ele = element.findElement(By.xpath(path));
                        break;
                    case "css":
                        ele = element.findElement(By.cssSelector(path));
                        break;
                    case "id":
                        ele = element.findElement(By.id(path));
                        break;
                    case "name":
                        ele = element.findElement(By.name(path));
                        break;
                    case "tagname":
                        ele = element.findElement(By.tagName(path));
                        break;
                }
                break;
            }
        }

        if(flag == false){
            throw new NoSuchElementException("无法定位元素：" + element.toString() + ".findElement(By." + way + "(" + path + "))");
        }
        return ele;

    }

    public static WebElement findByXpath(WebElement element, String path){
        return findBy(element, path, "xpath");
    }

    public static WebElement findByCss(WebElement element, String path){
        return findBy(element, path, "css");
    }

    public static WebElement findByID(WebElement element, String path){
        return findBy(element, path, "id");
    }

    public static WebElement findByName(WebElement element, String path){
        return findBy(element, path, "name");
    }

    public static WebElement findByTagName(WebElement element, String path){
        return findBy(element, path, "tagname");
    }






    //等待元素可访问(不一定要可见)后，滚动到元素可见的位置，再返回该元素。
    public static WebElement scrollAndFindByXpath(String path) throws Exception{
        loadingBarBGDieAway();
        //等待元素出现
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(path)));
        //拖动滚动条到元素能出现的地方
        String js = "arguments[0].scrollIntoView(true);";
        ((JavascriptExecutor) driver).executeScript(js, element);
        return element;
    }

    public static WebElement scrollAndFindByCss(String path) throws Exception{
        loadingBarBGDieAway();
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(path)));
        String js = "arguments[0].scrollIntoView(true);";
        ((JavascriptExecutor) driver).executeScript(js, element);
        return element;
    }

    public static WebElement scrollAndFindByID(String path) throws Exception{
        loadingBarBGDieAway();
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.id(path)));
        String js = "arguments[0].scrollIntoView(true);";
        ((JavascriptExecutor) driver).executeScript(js, element);
        return element;
    }

    public static WebElement scrollAndFindByName(String path) throws Exception{
        loadingBarBGDieAway();
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.name(path)));
        String js = "arguments[0].scrollIntoView(true);";
        ((JavascriptExecutor) driver).executeScript(js, element);
        return element;
    }

    public static WebElement scrollAndFindByTagName(String path) throws Exception{
        loadingBarBGDieAway();
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.tagName(path)));
        String js = "arguments[0].scrollIntoView(true);";
        ((JavascriptExecutor) driver).executeScript(js, element);
        return element;
    }





    //通过已有元素再次定位另一元素，等待元素可访问(不一定要可见)后，滚动到元素可见的位置，再返回该元素。
    public static WebElement scrollAndFindByXpath(WebElement element, String path) throws Exception{
        //等待元素出现
        WebElement ele = findByXpath(element, path);
        //拖动滚动条到元素能出现的地方
        String js = "arguments[0].scrollIntoView(true);";
        ((JavascriptExecutor) driver).executeScript(js, ele);
        return ele;
    }

    public static WebElement scrollAndFindByCss(WebElement element, String path) throws Exception{
        WebElement ele = findByCss(element, path);
        String js = "arguments[0].scrollIntoView(true);";
        ((JavascriptExecutor) driver).executeScript(js, ele);
        return ele;
    }

    public static WebElement scrollAndFindByID(WebElement element, String path) throws Exception{
        WebElement ele = findByID(element, path);
        String js = "arguments[0].scrollIntoView(true);";
        ((JavascriptExecutor) driver).executeScript(js, ele);
        return ele;
    }

    public static WebElement scrollAndFindByName(WebElement element, String path) throws Exception{
        WebElement ele = findByName(element, path);
        String js = "arguments[0].scrollIntoView(true);";
        ((JavascriptExecutor) driver).executeScript(js, ele);
        return ele;
    }

    public static WebElement scrollAndFindByTagName(WebElement element, String path) throws Exception{
        WebElement ele = findByTagName(element, path);
        String js = "arguments[0].scrollIntoView(true);";
        ((JavascriptExecutor) driver).executeScript(js, ele);
        return ele;
    }






    //等待页面加载完毕
    public static void pageLoaded(){
        wait.until(new ExpectedCondition<Boolean>() {
            public Boolean apply(WebDriver driver){
                return ((JavascriptExecutor)driver).executeScript("return document.readyState").equals("complete");
            }
        });
    }



    /**
     * 等待所在frame加载完毕(适用于frame东西多，加载慢的情况)
     * @param by frame元素路径
     * @param pos 为0意即确认是单个frame；pos为>0意即会找到一组符合的frame，下标从1开始选择
     * @return 返回该frame
     */
    public static WebElement frameLoaded(By by, int pos){
        String js = "var newp=document.createElement('p');" +
                "newp.setAttribute('id', 'newpflag');" +
                "newp.setAttribute('value', 'loading');" +
                "document.body.appendChild(newp); " +
                "arguments[0].onload = function(){newp.setAttribute('value', 'complete'); }; ";

        WebElement element;
        //多半问题出在这里：在这一步的时候，frame已经加载完毕了，不会再去做onload事件
        if(pos == 0) {
            element = driver.findElement(by);
        }
        else{
            element = driver.findElements(by).get(pos-1);
        }
        System.out.println("frame:" + element);

        ((JavascriptExecutor) driver).executeScript(js, element);
        wait.until(new ExpectedCondition<Boolean>() {
            public Boolean apply(WebDriver driver){
//                String js = "return arguments[0].contentDocument.readyState";
                System.out.println("执行frameLoaded()的判断一次");
                String str = "var np = document.getElementById('newpflag'); " +
                        "return np.getAttribute('value')";
                System.out.println(((JavascriptExecutor) driver).executeScript(str));
                return ((JavascriptExecutor) driver).executeScript(str).equals("complete");
            }
        });
        System.out.println("当前frame加载完毕");
        return element;
    }


    /**
     * 集合了等待页面和frame加载完毕
     * @param by frame元素路径
     * @param pos 为0意即确认是单个frame；pos为>0意即会找到一组符合的frame，下标从1开始选择
     */
    public static void pageAndFrameLoaded(By by, int pos){
        pageLoaded();
        frameLoaded(by, pos);
    }


    /**
     * 判断已加载完成的页面上是否含有两个元素中的一个(用于判断页面在哪里)，有谁返回谁，两个都没有抛异常，两个都有返回第一个
     * @param by1
     * @param by2
     * @return
     * @throws Exception
     */
    public static WebElement find1In2(By by1, By by2) throws Exception{
        pageLoaded();
        WebElement element = null;
        try {
            element = driver.findElement(by1);
        }catch (NoSuchElementException e1){
            try {
                element = driver.findElement(by2);
            }catch (NoSuchElementException e2){
                throw new Exception("页面没能在给出的两个位置中定位到相应元素！");
            }
        }
        return element;

    }

    public static WebElement find1In2V2(By by1, By by2) throws Exception {
        //等待直到出现两个元素中的一个
        wait.until(new ExpectedCondition<Boolean>() {

            Boolean find(WebDriver dr, By b1, By b2){

                Boolean b1Flag = false, b2Flag = false;
                try{
                    dr.findElement(b1);
                    b1Flag = true;
                }catch (NoSuchElementException e1){
                    try{
                        dr.findElement(b2);
                        b2Flag = true;
                    }catch (NoSuchElementException e2){

                    }
                }
                if(b1Flag || b2Flag){
                    return true;
                }
                else{
                    return false;
                }
            }

            public Boolean apply(WebDriver driver){
                return find(driver, by1, by2);
            }
        });


        //有元素出现了，则获取出现了的元素并返回
        WebElement element = null;
        try {
            element = driver.findElement(by1);
        }catch (NoSuchElementException e1){
            try {
                element = driver.findElement(by2);
            }catch (NoSuchElementException e2){
                throw new Exception("页面没能在给出的两个位置中定位到相应元素！");
            }
        }
        return element;

    }


    /**
     * 打开一个页面会有加载中的层，这个会影响页面操作
     * 这个方法用于检查它是否消失了，消失了才可做其它事情
     * 若没有出现，则啥都不做
     * 暂没有使用，因为发现控制后边一个层才有效，见loadingBarBGDieAway
     */
    @Deprecated
    public static void loadingBarDieAway(){
        driver.switchTo().defaultContent();
        //页面上有可能不止一个ui_loading_progressBar，要在此处理,得到唯一显示的那个(当前的这个)
        //若一个都没显示，则啥都不做了
        List<WebElement> list = findAllByID("ui_loading_progressBar");
        WebElement bar = null;
        WebElement b;
        Iterator<WebElement> it = list.iterator();
        while(it.hasNext()){
            b = it.next();
            System.out.println("发现了一个加载中：" + b);
            if(b.isDisplayed()){
                bar = b;
                break;
            }
        }

        //等待当前的这个消失。消失了才往后执行
        if(bar != null) {
            System.out.println("**************得到了正在显示中的‘加载中...’" + bar);
            wait.until(ExpectedConditions.invisibilityOf(bar));
            System.out.println("**************‘加载中...’消失了");
        }
        else{
            System.out.println("**************没有发现正在显示中的‘加载中...’");
        }

    }

    /**
     * 前提：driver已经进入了待检查的frame里
     * 打开一个页面会有加载中的后边的层，这个会影响页面操作
     * 这个方法用于检查它是否消失了，消失了才可做其它事情
     * 若没有出现，甚至那个层都不存在，则啥都不做
     */
    public static void loadingBarBGDieAway() {
        WebElement element;
        try{
            element = driver.findElement(By.id("ui_loading_overlay"));
            if(element.isDisplayed()) {
//            WebElement barBG = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("ui_loading_overlay")));
                //不判断元素是否存在，直接判断元素是否可见
                wait.until(ExpectedConditions.invisibilityOf(element));
            }
        }
        catch (Exception e){
            return;
        }


    }

    /**
     * 跳转到某个frame。涉及到获取frame,跳转，检验是否有loading层出现的问题
     * @param by frame元素路径
     * @param pos 为0意即确认是单个frame；pos为>0意即会找到一组符合的frame，下标从1开始选择
     */
    public static void jumpIntoFrameCheckLoading(By by, int pos){
        WebElement frame;
        if(pos == 0) {
            frame = driver.findElement(by);
        }
        else{
            frame = driver.findElements(by).get(pos-1);
        }
        driver.switchTo().frame(frame);
        AutoWait.loadingBarBGDieAway();

    }


    /**
     * 等待某元素的某属性值是否变为了指定值。可以帮助处理类似异步ajax加载的控件需要的等待
     * 比如可以判断元素的style属性是否含有block字样
     * @param by 定位路径
     * @param attribute 属性名称
     * @param value 属性值
     */
    public void findAttributeValue(By by, String attribute, String value) {
        while(driver.findElement(by).getAttribute(attribute).contains(value)) {
            int timeout = 10;
            if(timeout > 0) {
                timeout--;
                try {
                    System.out.println(attribute + "t" + value);
                    Thread.sleep(1000);
                } catch(Exception e) {
                    System.out.println("元素属性加载失败。");
                }
            }
        }
    }


}
